package org.hawk.gwt.ppc.introspection.impl;

import java.lang.reflect.Constructor;
import java.util.Collections;
import java.util.List;

import org.hawk.gwt.ppc.introspection.JavaConstructor;
import org.hawk.gwt.ppc.introspection.JavaModifier;
import org.hawk.gwt.ppc.introspection.JavaTypeScope;
import org.hawk.gwt.ppc.introspection.JavaTypeVariable;
import org.hawk.gwt.ppc.parser.BlockDescriptor;
import org.hawk.gwt.ppc.parser.ConstructorDescriptor;
import org.hawk.gwt.ppc.parser.Descriptor;
import org.hawk.gwt.ppc.utils.Name;

/**
 * Represents java constructor in context of introspection API.
 * Provides both source and binary introspection logic.
 * 
 * @author alex.bereznevatiy@gmail.com
 *
 */
final class JavaConstcructorImpl extends AbstractJavaCallableMemberScope<ConstructorDescriptor> implements JavaConstructor {
	
	private Constructor<?> constructor;
	private List<JavaTypeScope> resolvedParams;
	private boolean defaultConstructor;
	
	/**
	 * Constructor for source introspection logic.
	 * @param descriptor
	 * @param parent
	 */
	JavaConstcructorImpl(ConstructorDescriptor descriptor, JavaTypeScope parent) {
		init(descriptor, (JavaScopeImpl)parent);
		this.defaultConstructor = false;
	}

	/**
	 * Constructor for binary introspection logic.
	 * @param constructor
	 * @param parent
	 */
	JavaConstcructorImpl(Constructor<?> constructor, JavaTypeScope parent) {
		init(null, (JavaScopeImpl)parent);
		this.constructor = constructor;
		this.defaultConstructor = false;
	}
	
	private JavaConstcructorImpl(JavaTypeScope parent) {
		init(null, (JavaScopeImpl)parent);
		this.defaultConstructor = true;
		setModifiers(Collections.<JavaModifier>singletonList(JavaModifier.PUBLIC));
	}

	/**
	 * @param sourceJavaTypeScope
	 * @return default constructor (public with no parameters)
	 */
	static JavaConstructor createDefault(JavaTypeScope parent) {
		return new JavaConstcructorImpl(parent);
	}

	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.introspection.JavaCallableMember#isVarArgs()
	 */
	public boolean isVarArgs() {
		if (defaultConstructor) {
			return false;
		}
		if (constructor != null) {
			return constructor.isVarArgs();
		}
		return getDescriptor().isVarArgs();
	}
	
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.introspection.JavaCallableMember#getParametersTypes()
	 */
	public List<JavaTypeScope> getParametersTypes() {
		if (defaultConstructor) {
			return Collections.emptyList();
		}
		if (resolvedParams != null) {
			return resolvedParams;
		}
		if (constructor != null) {
			resolvedParams = getBinaryParametersTypes(constructor.getParameterTypes());
		} else {
			resolvedParams = getSourceParametersTypes(false);
		}
		return resolvedParams;
	}
	
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.introspection.impl.AbstractJavaCallableMemberScope#createParametersList(int)
	 */
	@Override
	List<JavaTypeScope> createParametersList(int size) {
		JavaTypeScope type = (JavaTypeScope) getParent();
		if (!type.isEnum()) {
			return super.createParametersList(size);
		}
		List<JavaTypeScope> result = super.createParametersList(size + 2);
		result.add(PrimitiveTypeScopes.STRING);
		result.add(PrimitiveTypeScopes.INT);
		return result;
	}
	
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.introspection.JavaCallableMember#getGenericParameterTypes()
	 */
	public List<JavaTypeScope> getGenericParameterTypes() {
		if (defaultConstructor) {
			return Collections.emptyList();
		}
		if (constructor != null) {
			return getBinaryParametersTypes(constructor.getGenericParameterTypes());
		}
		return getSourceParametersTypes(true);
	}
	
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.introspection.JavaParameterizable#getTypeVariables()
	 */
	public List<JavaTypeVariable> getTypeVariables() {
		if (defaultConstructor) {
			return Collections.emptyList();
		}
		if (constructor != null) {
			return GenericHelper.buildTypeVariables(constructor.getTypeParameters(), this);
		}
		return GenericHelper.buildTypeVariables(getDescriptor().getTypeParameters(), this);
	}

	/* (non-Javadoc)
	 * @see org.hawk.gwt.ppc.introspection.impl.AbstractJavaCallableMemberScope#getName()
	 */
	@Override
	String getName() {
		return Name.getSimpleName(((JavaTypeScope)getParent()).getName());
	}
	
	/* (non-Javadoc)
	 * @see org.hawk.gwt.ppc.introspection.impl.JavaScopeImpl#parseChildren(org.hawk.gwt.ppc.parser.Descriptor)
	 */
	@Override
	void parseChildren(Descriptor descriptor) {
		BlockDescriptor body = ((ConstructorDescriptor)descriptor).getBody();
		if (body != null) {
			super.parseChildren(body);
		}
	}
}
